{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### LangChain表达式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Why did the bear cross the playground? To get to the slide!'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Sample code\n",
    "\n",
    "from langchain.prompts import ChatPromptTemplate # 导入模板类\n",
    "from langchain.chat_models import ChatOpenAI \n",
    "from langchain.schema.output_parser import StrOutputParser # 导入字符串解析器\n",
    "\n",
    "# 实例化提示词模板和聊天模型包装器\n",
    "prompt = ChatPromptTemplate.from_template(\"tell me a joke about {topic}\")\n",
    "model = ChatOpenAI(\n",
    "    openai_api_base=\"http://localhost:8000/v1\",\n",
    "    openai_api_key=\"none\",\n",
    "    streaming=True,\n",
    "    verbose=True,\n",
    "    model_name=\"Qwen/Qwen1.5-4B-Chat\"\n",
    ")\n",
    "\n",
    "# 定义处理链\n",
    "chain = prompt | model | StrOutputParser() # 使用管道操作符 | 连接模块\n",
    "# 调用处理链\n",
    "response = chain.invoke({\"topic\":\"bears\"}) # 请注意，invoke方法传入的参数是一个字典，其中每一个键值对应提示词内的占位符\n",
    "\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 提示词模板+模型包装器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='Why did the bear put on his party hat?\\n\\nBecause he wanted to get everyone \"polarized.\"')"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 重新定义处理链\n",
    "chain = prompt | model \n",
    "\n",
    "# 调用处理链\n",
    "response = chain.invoke({\"topic\":\"bears\"})\n",
    "\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='Why did the bear refuse to move? ')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用带有stop的新链\n",
    "chain = prompt | model.bind(stop=[\"\\n\"]) # 请注意，stop仅仅代表模型的输出将抵达的终点\n",
    "response = chain.invoke({\"topic\":\"bears\"})\n",
    "response\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"Why couldn't the bear sit on the rug? Because it was flat as a pancake!\")"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 由于bind方法同样支持OpenAI的函数回调功能，可以将函数描述列表绑定到模型包装器上\n",
    "functions = [\n",
    "    {\n",
    "        \"name\":\"joke\",\n",
    "        \"description\":\"A joke\",\n",
    "        \"parameters\":{\n",
    "            \"type\":\"object\",\n",
    "            \"properties\":{\n",
    "                \"setup\":{\n",
    "                    \"type\":\"string\",\n",
    "                    \"description\":\"The setup for the joke\"\n",
    "                }\n",
    "            },\n",
    "            \"punchline\":{\n",
    "                \"type\":\"string\",\n",
    "                \"description\":\"The punchline for the joke\"\n",
    "            },\n",
    "            \"required\":[\"setup\",\"punchline\"]\n",
    "        }\n",
    "    }\n",
    "]\n",
    "\n",
    "# 重新定义处理链\n",
    "chain = prompt | model.bind(function_call = {\"name\":\"joke\"},functions=functions)\n",
    "\n",
    "response = chain.invoke({\"topic\":\"bears\"},config={})\n",
    "\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 提示词模板+模型包装器+输出解析器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Why did the bear refuse to move? \\nBecause it was stuck in the frozen bear river.'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为链导入字符串解析器\n",
    "chain = prompt | model | StrOutputParser()\n",
    "response = chain.invoke({\"topic\":\"bears\"},config={})\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 多功能组合链"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'美国总统巴拉克·奥巴马来自美国夏威夷州火奴鲁鲁市。'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from operator import itemgetter\n",
    "# 第一个提示词用于获取一个人来自于哪个城市\n",
    "prompt1 = ChatPromptTemplate.from_template(\"what is the city {person} is from?\")\n",
    "# 第二个提示词用于获取一个城市所在的国家\n",
    "prompt2 = ChatPromptTemplate.from_template(\"What country is the city {city} in?respond in {language}\")\n",
    "# 组合第一个链，用于获取城市\n",
    "chain1 = prompt1 | model | StrOutputParser() # 限制使用StrOutputParser()来控制输出仅为字符串\n",
    "# 组合第二个链，其中输入分别是第一个链返回的城市信息和从invoke中拿到的语言信息\n",
    "# 接着将输入的参数填充进第二个提示词中\n",
    "# 最后模型输出，并交由字符串解析器处理\n",
    "chain2 = {\"city\":chain1,\"language\":itemgetter(\"language\")} | prompt2 | model | StrOutputParser()\n",
    "\n",
    "chain2.invoke({\"person\":\"obama\",\"language\":\"chinese\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
